// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/accessibility/platform/automation/automation_api_util.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_event_generator.h"
#include "ui/accessibility/ax_position.h"

namespace ui {

bool ShouldIgnoreAXEventForAutomation(ax::mojom::Event event_type) {
  // Important note: if you are getting here as a result of a compilation error
  // while adding or removing enum values from ax::mojom::Event, please ensure
  // you keep that enum in sync with EventType in
  // extensions/common/api/automation.idl
  // The stringified enum value gets mapped directly from one enum to another.
  switch (event_type) {
    // Generated by AXEventGenerator. This list have values we're interested
    // from the intersection of AXEventGenerator::Event and
    // ax::mojom::Event.
    case ax::mojom::Event::kActiveDescendantChanged:
    case ax::mojom::Event::kAriaAttributeChanged:
    case ax::mojom::Event::kCheckedStateChanged:
    case ax::mojom::Event::kChildrenChanged:
    case ax::mojom::Event::kDocumentSelectionChanged:
    case ax::mojom::Event::kDocumentTitleChanged:
    case ax::mojom::Event::kExpandedChanged:
    case ax::mojom::Event::kRowCollapsed:
    case ax::mojom::Event::kRowCountChanged:
    case ax::mojom::Event::kRowExpanded:
    case ax::mojom::Event::kSelectedChildrenChanged:
      return true;

      // All other ax events.
    case ax::mojom::Event::kNone:
    case ax::mojom::Event::kAlert:
    case ax::mojom::Event::kAutocorrectionOccured:
    case ax::mojom::Event::kBlur:
    case ax::mojom::Event::kClicked:
    case ax::mojom::Event::kControlsChanged:
    case ax::mojom::Event::kEndOfTest:
    case ax::mojom::Event::kFocus:
    case ax::mojom::Event::kFocusAfterMenuClose:
    case ax::mojom::Event::kFocusContext:
    case ax::mojom::Event::kHide:
    case ax::mojom::Event::kHitTestResult:
    case ax::mojom::Event::kHover:
    case ax::mojom::Event::kImageFrameUpdated:
    case ax::mojom::Event::kLayoutComplete:
    case ax::mojom::Event::kLiveRegionCreated:
    case ax::mojom::Event::kLiveRegionChanged:
    case ax::mojom::Event::kLoadComplete:
    case ax::mojom::Event::kLoadStart:
    case ax::mojom::Event::kLocationChanged:
    case ax::mojom::Event::kMediaStartedPlaying:
    case ax::mojom::Event::kMediaStoppedPlaying:
    case ax::mojom::Event::kMenuEnd:
    case ax::mojom::Event::kMenuListValueChanged:
    case ax::mojom::Event::kMenuPopupEnd:
    case ax::mojom::Event::kMenuPopupStart:
    case ax::mojom::Event::kMenuStart:
    case ax::mojom::Event::kMouseCanceled:
    case ax::mojom::Event::kMouseDragged:
    case ax::mojom::Event::kMouseMoved:
    case ax::mojom::Event::kMousePressed:
    case ax::mojom::Event::kMouseReleased:
    case ax::mojom::Event::kScrolledToAnchor:
    case ax::mojom::Event::kScrollPositionChanged:
    case ax::mojom::Event::kSelection:
    case ax::mojom::Event::kSelectionAdd:
    case ax::mojom::Event::kSelectionRemove:
    case ax::mojom::Event::kShow:
    case ax::mojom::Event::kStateChanged:
    case ax::mojom::Event::kTextChanged:
    case ax::mojom::Event::kWindowActivated:
    case ax::mojom::Event::kWindowDeactivated:
    case ax::mojom::Event::kWindowVisibilityChanged:
    case ax::mojom::Event::kTextSelectionChanged:
    case ax::mojom::Event::kTooltipClosed:
    case ax::mojom::Event::kTooltipOpened:
    case ax::mojom::Event::kTreeChanged:
    case ax::mojom::Event::kValueChanged:
      return false;
  }

  NOTREACHED();
  return false;
}

bool ShouldIgnoreGeneratedEventForAutomation(
    AXEventGenerator::Event event_type) {
  // Important note: if you are getting here as a result of a compilation error
  // while adding or removing enum values from AXEventGenerator::Event,
  // please ensure you keep that enum in sync with EventType in
  // extensions/common/api/automation.idl
  // The stringified enum value gets mapped directly from one enum to another.
  switch (event_type) {
      // These enum values should be mapped to automation.idl.
    case AXEventGenerator::Event::ACCESS_KEY_CHANGED:
    case AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED:
    case AXEventGenerator::Event::ALERT:
    case AXEventGenerator::Event::ARIA_CURRENT_CHANGED:
    case AXEventGenerator::Event::ATOMIC_CHANGED:
    case AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
    case AXEventGenerator::Event::AUTOFILL_AVAILABILITY_CHANGED:
    case AXEventGenerator::Event::BUSY_CHANGED:
    case AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
    case AXEventGenerator::Event::CHECKED_STATE_CHANGED:
    case AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
    case AXEventGenerator::Event::CHILDREN_CHANGED:
    case AXEventGenerator::Event::CLASS_NAME_CHANGED:
    case AXEventGenerator::Event::COLLAPSED:
    case AXEventGenerator::Event::CONTROLS_CHANGED:
    case AXEventGenerator::Event::DETAILS_CHANGED:
    case AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
    case AXEventGenerator::Event::DESCRIPTION_CHANGED:
    case AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED:
    case AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
    case AXEventGenerator::Event::DROPEFFECT_CHANGED:
    case AXEventGenerator::Event::EDITABLE_TEXT_CHANGED:
    case AXEventGenerator::Event::ENABLED_CHANGED:
    case AXEventGenerator::Event::EXPANDED:
    case AXEventGenerator::Event::FOCUS_CHANGED:
    case AXEventGenerator::Event::FLOW_FROM_CHANGED:
    case AXEventGenerator::Event::FLOW_TO_CHANGED:
    case AXEventGenerator::Event::GRABBED_CHANGED:
    case AXEventGenerator::Event::HASPOPUP_CHANGED:
    case AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
    case AXEventGenerator::Event::IGNORED_CHANGED:
    case AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
    case AXEventGenerator::Event::INVALID_STATUS_CHANGED:
    case AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED:
    case AXEventGenerator::Event::LABELED_BY_CHANGED:
    case AXEventGenerator::Event::LANGUAGE_CHANGED:
    case AXEventGenerator::Event::LAYOUT_INVALIDATED:
    case AXEventGenerator::Event::LIVE_REGION_CHANGED:
    case AXEventGenerator::Event::LIVE_REGION_CREATED:
    case AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED:
    case AXEventGenerator::Event::LIVE_RELEVANT_CHANGED:
    case AXEventGenerator::Event::LIVE_STATUS_CHANGED:
    case AXEventGenerator::Event::MENU_POPUP_END:
    case AXEventGenerator::Event::MENU_POPUP_START:
    case AXEventGenerator::Event::MENU_ITEM_SELECTED:
    case AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
    case AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
    case AXEventGenerator::Event::NAME_CHANGED:
    case AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
    case AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
    case AXEventGenerator::Event::PARENT_CHANGED:
    case AXEventGenerator::Event::PLACEHOLDER_CHANGED:
    case AXEventGenerator::Event::PORTAL_ACTIVATED:
    case AXEventGenerator::Event::POSITION_IN_SET_CHANGED:
    case AXEventGenerator::Event::RELATED_NODE_CHANGED:
    case AXEventGenerator::Event::READONLY_CHANGED:
    case AXEventGenerator::Event::REQUIRED_STATE_CHANGED:
    case AXEventGenerator::Event::ROLE_CHANGED:
    case AXEventGenerator::Event::ROW_COUNT_CHANGED:
    case AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
    case AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
    case AXEventGenerator::Event::SELECTED_CHANGED:
    case AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED:
    case AXEventGenerator::Event::SELECTED_VALUE_CHANGED:
    case AXEventGenerator::Event::TEXT_SELECTION_CHANGED:
    case AXEventGenerator::Event::SET_SIZE_CHANGED:
    case AXEventGenerator::Event::SORT_CHANGED:
    case AXEventGenerator::Event::STATE_CHANGED:
    case AXEventGenerator::Event::SUBTREE_CREATED:
    case AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
    case AXEventGenerator::Event::VALUE_IN_TEXT_FIELD_CHANGED:
    case AXEventGenerator::Event::RANGE_VALUE_CHANGED:
    case AXEventGenerator::Event::RANGE_VALUE_MAX_CHANGED:
    case AXEventGenerator::Event::RANGE_VALUE_MIN_CHANGED:
    case AXEventGenerator::Event::RANGE_VALUE_STEP_CHANGED:
      return false;

      // These enum values can be ignored and should not be mapped.
    case AXEventGenerator::Event::NONE:
    case AXEventGenerator::Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED:
    case AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
      return true;
  }

  NOTREACHED();
  return false;
}

std::tuple<ax::mojom::Event, AXEventGenerator::Event>
MakeTupleForAutomationFromEventTypes(
    const ax::mojom::Event& ax_event,
    const AXEventGenerator::Event& generated_event) {
  std::tuple<ax::mojom::Event, AXEventGenerator::Event> result;
  // Prefer generated events if they exist.
  if (generated_event != AXEventGenerator::Event::NONE) {
    result = std::make_tuple(ax::mojom::Event::kNone, generated_event);
  } else {
    // For events that are available as AX events and generated events, use the
    // generated type.
    AXEventGenerator::Event equivalent_event = AXEventGenerator::Event::NONE;
    MaybeParseGeneratedEvent(ToString(ax_event), &equivalent_event);
    if (equivalent_event != AXEventGenerator::Event::NONE)
      result = std::make_tuple(ax::mojom::Event::kNone, equivalent_event);
    else
      result = std::make_tuple(ax_event, AXEventGenerator::Event::NONE);
  }

  // At most one of the AXEvent / generated event should be populated.
  DCHECK(std::get<0>(result) == ax::mojom::Event::kNone ||
         std::get<1>(result) == AXEventGenerator::Event::NONE);

  return result;
}

std::tuple<ax::mojom::Event, AXEventGenerator::Event>
AutomationEventTypeToAXEventTuple(const char* event_type_string) {
  // Prefer generated event types if they exist.
  AXEventGenerator::Event generated_event = AXEventGenerator::Event::NONE;
  MaybeParseGeneratedEvent(event_type_string, &generated_event);
  if (generated_event != AXEventGenerator::Event::NONE) {
    return std::tuple<ax::mojom::Event, AXEventGenerator::Event>(
        ax::mojom::Event::kNone, generated_event);
  }

  // Otherwise use the AX event type.
  ax::mojom::Event ax_event = ax::mojom::Event::kNone;
  MaybeParseAXEnum<ax::mojom::Event>(event_type_string, &ax_event);
  return std::tuple<ax::mojom::Event, AXEventGenerator::Event>(
      ax_event, AXEventGenerator::Event::NONE);
}

AXPositionKind StringToAXPositionKind(const std::string& type) {
  if (type == "tree") {
    return AXPositionKind::TREE_POSITION;
  } else if (type == "text") {
    return AXPositionKind::TEXT_POSITION;
  }

  return AXPositionKind::NULL_POSITION;
}

}  // namespace ui
